package com.alibaba.csp.sentinel.dashboard.util;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 雪花算法 ID自增工具
 * @author SUNP
 *
 */
public class IdUtils {
	
	  private static final Logger logger = LoggerFactory.getLogger(IdUtils.class);

	    private static final Pattern PATTERN_LONG_ID = Pattern.compile("^([0-9]{15})([0-9a-f]{32})([0-9a-f]{3})$");

	    private static final Pattern PATTERN_HOSTNAME = Pattern.compile("^.*\\D+([0-9]+)$");

	    private static final long OFFSET = LocalDate.of(2000, 1, 1).atStartOfDay(ZoneId.of("Z")).toEpochSecond();

	    private static final long MAX_NEXT = 0b11111_11111111_111L;

	    private static final long SHARD_ID = getServerIdAsLong();

	    private static long offset = 0;

	    private static long lastEpoch = 0;

	    /**
	     * 生成16位不重复id
	     * @return
	     */
	    public static long nextId() {
	        return nextId(System.currentTimeMillis() / 1000);
	    }

	    private static synchronized long nextId(long epochSecond) {
	        if (epochSecond < lastEpoch) {
	            // warning: clock is turn back:
	            logger.warn("clock is back: " + epochSecond + " from previous:" + lastEpoch);
	            epochSecond = lastEpoch;
	        }
	        if (lastEpoch != epochSecond) {
	            lastEpoch = epochSecond;
	            reset();
	        }
	        offset++;
	        long next = offset & MAX_NEXT;
	        if (next == 0) {
	            logger.warn("maximum id reached in 1 second in epoch: " + epochSecond);
	            return nextId(epochSecond + 1);
	        }
	        return generateId(epochSecond, next, SHARD_ID);
	    }

	    private static void reset() {
	        offset = 0;
	    }

	    /**
	     * 生成id
	     * @param epochSecond
	     * @param next
	     * @param shardId
	     * @return
	     */
	    private static long generateId(long epochSecond, long next, long shardId) {
	        return ((epochSecond - OFFSET) << 21) | (next << 5) | shardId;
	    }

	    /**
	     * 获取机器Id
	     * @return
	     */
	    private static long getServerIdAsLong() {
	        try {
	            //获取主机名
	            String hostname = InetAddress.getLocalHost().getHostName();
	            Matcher matcher = PATTERN_HOSTNAME.matcher(hostname);
	            if (matcher.matches()) {
	                long n = Long.parseLong(matcher.group(1));
	                if (n >= 0 && n < 8) {
	                    logger.info("detect server id from host name {}: {}.", hostname, n);
	                    return n;
	                }
	            }
	        } catch (UnknownHostException e) {
	            logger.warn("unable to get host name. set server id = 0.");
	        }
	        return 0;
	    }
	    
	    public static void main(String args[]){
	        for (int i=0; i < 1000000; i++){
	            System.out.println(nextId());
	        }
	    }
}
